#region

using System;
using System.Collections.Generic;
using System.Linq;
using SFML.Graphics;
using SFML.Window;
using SFMLStart.Data;
using TimeDRODPOF.TDComponents;
using VeeTileEngine2012;

#endregion

namespace TimeDRODPOF.TDLib
{
    public static class TDLFactory
    {
        public static TDGame Game { private get; set; }
        public static Field Field { get { return Game.Field; } }
        public static Tile Tile { private get; set; }

        [EditorOutline(-1, "PlayerStart", TDLLayers.Roach, @"editor\humanoid\yellow", 3)]
        public static Entity StartPoint(int mDirection = 0) { return new Entity(Tile, false) {Layer = TDLLayers.Roach}; }

        [EditorOutline(0, "Wall", TDLLayers.Wall, @"environment\wall\purple", 0, "walltiles", "single")]
        public static Entity Wall(bool mIsBroken = false)
        {
            var result = new Entity(Tile) {Layer = TDLLayers.Wall};

            var cRender = new TDCRender(Assets.Tilesets["walltiles"].GetSprite("single", Assets.GetTexture(@"environment\wall\purple")));
            var cHitByWeapon = new TDCHitByWeapon(mWeapon => TDLMethods.Break(mIsBroken, result));
            var cRecalculateSprites = new TDCRecalculateSprites(TDLTags.Wall, TDLHelper.RecalculateWallSprite);

            if (mIsBroken) TDLMethods.AttachBrokenOverlay(cRender);

            result.AddTags(TDLTags.Solid, TDLTags.Wall, TDLTags.HitByWeapon);
            result.AddComponents(cRender, cHitByWeapon, cRecalculateSprites);

            return result;
        }

        [EditorOutline(1, "WallBroken", TDLLayers.Wall, @"editor\wall\broken", 0)]
        public static Entity WallBroken() { return Wall(true); }

        [EditorOutline(2, "Pit", TDLLayers.Pit, @"environment\pit\black", 0, "doortiles", "fill")]
        public static Entity Pit()
        {
            var result = new Entity(Tile) {Layer = TDLLayers.Pit};

            var cRender = new TDCRender(Assets.Tilesets["walltiles"].GetSprite("single", Assets.GetTexture(@"environment\pit\black")));
            var cRecalculateSprites = new TDCRecalculateSprites(TDLTags.Pit, TDLHelper.RecalculateDoorSprite);

            result.AddTags(TDLTags.Solid, TDLTags.Pit);
            result.AddComponents(cRender, cRecalculateSprites);

            return result;
        }

        [EditorOutline(3, "Trapdoor", TDLLayers.Trapdoor, @"environment\trapdoor", 0)]
        public static Entity Trapdoor(bool mIsOff = false, List<int> mIDs = default(List<int>), bool mIsOnWater = false, bool mIsBroken = false)
        {
            var result = new Entity(Tile) {Layer = TDLLayers.Trapdoor, UpdateOrder = TDLOrders.Trapdoor};

            var trapdoorTexture = @"environment\trapdoor";
            if (mIsOnWater) trapdoorTexture = @"environment\watertrapdoor";

            var cRender = new TDCRender(Assets.Tilesets["onofftiles"].GetSprite("on", Assets.GetTexture(trapdoorTexture)));
            var cID = mIDs != null ? new TDCID(mIDs.ToArray()) : new TDCID(-1);
            var cSwitch = new TDCSwitch(mIsOff) {OffTextureRect = Assets.Tilesets["onofftiles"].GetTextureRect("off"), OnTextureRect = Assets.Tilesets["onofftiles"].GetTextureRect("on")};
            var cIDSwitchAI = new TDCIDSwitchAI(cSwitch, cID);
            var cSpecialSquare = new TDCSpecialSquare();

            if (mIsBroken) TDLMethods.AttachCrackedOverlay(cRender, 0);

            result.AddTags(TDLTags.GroundWalkable, TDLTags.Trapdoor);
            result.AddComponents(cRender, cID, cSwitch, cIDSwitchAI, cSpecialSquare);

            cSpecialSquare.OnMoveFromAllowed += (Entity mEntity, int mX, int mY, bool mNextSuccess, out bool mAbort) =>
                                                TDLMethods.TrapdoorOnMoveFrom(result, cSwitch.IsOff, mNextSuccess, mEntity, out mAbort, mIsOnWater, mIsBroken);

            return result;
        }

        [EditorOutline(25, "WaterTrapdoor", TDLLayers.Trapdoor, @"environment\watertrapdoor", 0)]
        public static Entity WaterTrapdoor(bool mIsOff = false, List<int> mIDs = default(List<int>), bool mIsBroken = false) { return Trapdoor(mIsOff, mIDs, true, mIsBroken); }

        [EditorOutline(4, "OrthogonalSquare", TDLLayers.Arrow, @"environment\orthogonalsquare", 1)]
        public static Entity OrthogonalSquare(bool mIsOff = false, List<int> mIDs = default(List<int>))
        {
            var result = new Entity(Tile) {Layer = TDLLayers.Arrow, UpdateOrder = TDLOrders.Trapdoor};

            var cRender = new TDCRender(Assets.Tilesets["onofftiles"].GetSprite("on", Assets.GetTexture(@"environment\orthogonalsquare")));
            var cSpecialSquare = new TDCSpecialSquare();
            var cID = mIDs != null ? new TDCID(mIDs.ToArray()) : new TDCID(-1);
            var cSwitch = new TDCSwitch(mIsOff) {OffTextureRect = Assets.Tilesets["onofftiles"].GetTextureRect("off"), OnTextureRect = Assets.Tilesets["onofftiles"].GetTextureRect("on")};
            var cIDSwitchAI = new TDCIDSwitchAI(cSwitch, cID);

            cSpecialSquare.OnMoveFromAllowed += (Entity mEntity, int mNextX, int mNextY, bool mNextSuccess, out bool mAbort) => TDLMethods.OrthogonalSquareMovement(mNextY, cSwitch, mNextX, out mAbort);
            cSpecialSquare.OnMoveToAllowed += (Entity mEntity, int mNextX, int mNextY, bool mNextSuccess, out bool mAbort) => TDLMethods.OrthogonalSquareMovement(mNextY, cSwitch, mNextX, out mAbort);

            result.AddTags(TDLTags.OrthogonalSquare);
            result.AddComponents(cRender, cSpecialSquare, cID, cSwitch, cIDSwitchAI);

            return result;
        }

        [EditorOutline(5, "DiagonalSquare", TDLLayers.Arrow, @"environment\diagonalsquare", 1)]
        public static Entity DiagonalSquare(bool mIsOff = false, List<int> mIDs = default(List<int>))
        {
            var result = new Entity(Tile) {Layer = TDLLayers.Arrow, UpdateOrder = TDLOrders.Trapdoor};

            var cRender = new TDCRender(Assets.Tilesets["onofftiles"].GetSprite("on", Assets.GetTexture(@"environment\diagonalsquare")));
            var cSpecialSquare = new TDCSpecialSquare();
            var cID = mIDs != null ? new TDCID(mIDs.ToArray()) : new TDCID(-1);
            var cSwitch = new TDCSwitch(mIsOff) {OffTextureRect = Assets.Tilesets["onofftiles"].GetTextureRect("off"), OnTextureRect = Assets.Tilesets["onofftiles"].GetTextureRect("on")};
            var cIDSwitchAI = new TDCIDSwitchAI(cSwitch, cID);

            cSpecialSquare.OnMoveFromAllowed += (Entity mEntity, int mNextX, int mNextY, bool mNextSuccess, out bool mAbort) => TDLMethods.DiagonalSquareMovement(mNextY, cSwitch, mNextX, out mAbort);
            cSpecialSquare.OnMoveToAllowed += (Entity mEntity, int mNextX, int mNextY, bool mNextSuccess, out bool mAbort) => TDLMethods.DiagonalSquareMovement(mNextY, cSwitch, mNextX, out mAbort);

            result.AddTags(TDLTags.DiagonalSquare);
            result.AddComponents(cRender, cSpecialSquare, cID, cSwitch, cIDSwitchAI);

            return result;
        }

        [EditorOutline(6, "Roach", TDLLayers.Roach, @"monster\roach", 3)]
        public static Entity Roach(int mDirection = 0)
        {
            var result = new Entity(Tile) {Layer = TDLLayers.Roach, UpdateOrder = TDLOrders.Roach};

            var cRender = new TDCRender(Assets.Tilesets["roachtiles"].GetSprite("n", Assets.GetTexture(@"monster\roach"))) {IsLerped = true};
            var cDirection = new TDCDirection {Direction = mDirection};
            var cTarget = new TDCTarget(TDLTags.MonsterTargetTags);
            var cHitByWeapon = new TDCHitByWeapon(mWeapon => TDLMethods.Kill(result));
            var cMovement = new TDCMovement(TDCMovement.MovementType.BeelineNormal, TDLTags.GroundAllowedTags, TDLTags.GroundObstacleTags, TDLTags.GroundExceptionTags);
            var cKiller = new TDCKiller(TDLTags.MonsterTargetTags, TDLMethods.Kill);
            var cMovementTargetAI = new TDCMovementTargetAI(Game, cMovement, cTarget, cDirection);
            var cRenderDirectionAI = new TDCRenderDirectionAI(cRender, cDirection, "roachtiles");

            result.AddTags(TDLTags.Solid, TDLTags.SideBad, TDLTags.HitByWeapon,
                           TDLTags.RequiredKill, TDLTags.BoosterUser, TDLTags.WeightLow);
            result.AddComponents(cRender, cDirection, cTarget, cHitByWeapon,
                                 cMovementTargetAI, cMovement, cKiller, cRenderDirectionAI);

            return result;
        }

        [EditorOutline(21, "RoachQueen", TDLLayers.Roach, @"monster\roachqueen", 3)]
        public static Entity RoachQueen(int mDirection = 0, int mSpawnTurns = 30)
        {
            var result = new Entity(Tile) {Layer = TDLLayers.Roach, UpdateOrder = TDLOrders.Roach};

            var turnsToSpawn = mSpawnTurns;

            var cRender = new TDCRender(Assets.Tilesets["roachtiles"].GetSprite("n", Assets.GetTexture(@"monster\roachqueen"))) {IsLerped = true};
            var cDirection = new TDCDirection {Direction = mDirection};
            var cTarget = new TDCTarget(TDLTags.MonsterTargetTags);
            var cHitByWeapon = new TDCHitByWeapon(mWeapon => TDLMethods.Kill(result));
            var cMovement = new TDCMovement(TDCMovement.MovementType.BeelineNormal, TDLTags.GroundAllowedTags, TDLTags.GroundObstacleTags, TDLTags.GroundExceptionTags) {IsReverse = true};
            var cMovementTargetAI = new TDCMovementTargetAI(Game, cMovement, cTarget, cDirection);
            var cBehavior = new TDCBehavior();
            var cRenderDirectionAI = new TDCRenderDirectionAI(cRender, cDirection, "roachtiles");

            cBehavior.OnNextTurn += mInput => TDLMethods.RoachQueenBehavior(mSpawnTurns, result, ref turnsToSpawn);

            result.AddTags(TDLTags.Solid, TDLTags.SideBad, TDLTags.HitByWeapon,
                           TDLTags.RequiredKill, TDLTags.BoosterUser, TDLTags.WeightLow);
            result.AddComponents(cRender, cDirection, cTarget, cHitByWeapon,
                                 cMovementTargetAI, cMovement, cBehavior, cRenderDirectionAI);

            return result;
        }

        [EditorOutline(7, "GreenDoor", TDLLayers.Door, @"environment\door\green\closed", 0, "doortiles", "single")]
        public static Entity GreenDoor(bool mIsOff = false)
        {
            var result = new Entity(Tile) {Layer = TDLLayers.Door};

            var recalculationNeeded = true;

            var cRender = new TDCRender(Assets.Tilesets["doortiles"].GetSprite("single", Assets.GetTexture(@"environment\door\green\closed")));
            var cRecalculateSprites = new TDCRecalculateSprites(TDLTags.DoorClosed, TDLHelper.RecalculateDoorSprite);
            var cSwitch = new TDCSwitch(mIsOff) {OffTextureName = @"environment\door\green\open", OnTextureName = @"environment\door\green\closed"};
            var cSwitchRecalculateTagAI = new TDCSwitchRecalculateTagAI(cSwitch, cRecalculateSprites, TDLTags.GreenDoorOpen, TDLTags.GreenDoorClosed);

            cSwitch.OnlyOffTags.Add(TDLTags.GreenDoorOpen);
            cSwitch.OnlyOnTags.AddRange(new[] {TDLTags.GreenDoorClosed, TDLTags.Solid});

            cSwitch.OnTurnOff += () => recalculationNeeded = true;
            cSwitch.OnTurnOn += () => recalculationNeeded = true;

            result.AddTags(TDLTags.GreenDoor);
            result.AddComponents(cRender, cSwitch, cSwitchRecalculateTagAI, cRecalculateSprites);

            if (Game.StructureControl.CurrentRoom != null && Game.StructureControl.CurrentRoom.IsClear) cSwitch.Toggle();

            result.Field.OnTurnStart += cRecalculateSprites.RecalculateNearbySprites;

            result.Field.OnTurnFinished += () =>
                                           {
                                               if (Game.StructureControl.CurrentRoom != null && Game.StructureControl.CurrentRoom.IsClear)
                                                   if (mIsOff) cSwitch.TurnOn();
                                                   else cSwitch.TurnOff();

                                               if (!recalculationNeeded) return;
                                               cRecalculateSprites.RecalculateNearbySprites();
                                               recalculationNeeded = false;
                                           };

            return result;
        }

        [EditorOutline(12, "GreenDoorOpen", TDLLayers.Door, @"environment\door\green\open", 0, "doortiles", "single")]
        public static Entity GreenDoorOpen() { return GreenDoor(true); }

        [EditorOutline(18, "BlueDoor", TDLLayers.Door, @"environment\door\blue\closed", 0, "doortiles", "single")]
        public static Entity BlueDoor(bool mIsOff = false)
        {
            var result = new Entity(Tile) {Layer = TDLLayers.Door};

            var recalculationNeeded = true;

            var cRender = new TDCRender(Assets.Tilesets["doortiles"].GetSprite("single", Assets.GetTexture(@"environment\door\blue\closed")));
            var cRecalculateSprites = new TDCRecalculateSprites(TDLTags.DoorClosed, TDLHelper.RecalculateDoorSprite);
            var cSwitch = new TDCSwitch(mIsOff) {OffTextureName = @"environment\door\blue\open", OnTextureName = @"environment\door\blue\closed"};
            var cSwitchRecalculateTagAI = new TDCSwitchRecalculateTagAI(cSwitch, cRecalculateSprites, TDLTags.BlueDoorOpen, TDLTags.BlueDoorClosed);

            cSwitch.OnlyOffTags.Add(TDLTags.BlueDoorOpen);
            cSwitch.OnlyOnTags.AddRange(new[] {TDLTags.BlueDoorClosed, TDLTags.Solid});

            cSwitch.OnTurnOff += () => recalculationNeeded = true;
            cSwitch.OnTurnOn += () => recalculationNeeded = true;

            result.AddTags(TDLTags.BlueDoor);
            result.AddComponents(cRender, cSwitch, cSwitchRecalculateTagAI, cRecalculateSprites);

            if (Game.IsLevelClear)
            {
                if (mIsOff) cSwitch.TurnOn();
                else cSwitch.TurnOff();
                result.Field.OnTurnStart += cRecalculateSprites.RecalculateNearbySprites;
            }

            result.Field.OnTurnFinished += () =>
                                           {
                                               if (!recalculationNeeded) return;
                                               cRecalculateSprites.RecalculateNearbySprites();
                                               recalculationNeeded = false;
                                           };
            return result;
        }

        [EditorOutline(19, "BlueDoorOpen", TDLLayers.Door, @"environment\door\blue\open", 0, "doortiles", "single")]
        public static Entity BlueDoorOpen() { return BlueDoor(true); }

        [EditorOutline(13, "RedDoor", TDLLayers.Door, @"environment\door\red\closed", 0, "doortiles", "single")]
        public static Entity RedDoor(bool mIsOff = false)
        {
            var result = new Entity(Tile) {Layer = TDLLayers.Door, UpdateOrder = TDLOrders.TrapdoorDoor};
            var wasTriggered = false;
            var recalculationNeeded = true;

            var cRender = new TDCRender(Assets.Tilesets["doortiles"].GetSprite("single", Assets.GetTexture(@"environment\door\red\closed")));
            var cRecalculateSprites = new TDCRecalculateSprites(TDLTags.DoorClosed, TDLHelper.RecalculateDoorSprite);
            var cSwitch = new TDCSwitch(mIsOff) {OffTextureName = @"environment\door\red\open", OnTextureName = @"environment\door\red\closed"};
            var cSwitchRecalculateTagAI = new TDCSwitchRecalculateTagAI(cSwitch, cRecalculateSprites, TDLTags.RedDoorOpen, TDLTags.RedDoorClosed);
            var cBehavior = new TDCBehavior();

            cSwitch.OnlyOffTags.Add(TDLTags.RedDoorOpen);
            cSwitch.OnlyOnTags.AddRange(new[] {TDLTags.RedDoorClosed, TDLTags.Solid});

            cSwitch.OnTurnOff += () => recalculationNeeded = true;
            cSwitch.OnTurnOn += () => recalculationNeeded = true;

            cBehavior.OnNextTurn += mInput =>
                                    {
                                        if (wasTriggered || result.Field.GetEntitiesByTag(TDLTags.Trapdoor).Any()) return;
                                        cSwitch.Toggle();

                                        TDLHelper.PlaySound("SoundRedDoor");
                                        wasTriggered = true;
                                    };

            result.AddTags(TDLTags.RedDoor);
            result.AddComponents(cRender, cSwitch, cSwitchRecalculateTagAI, cRecalculateSprites, cBehavior);

            result.Field.OnTurnFinished += () =>
                                           {
                                               if (!recalculationNeeded) return;
                                               cRecalculateSprites.RecalculateNearbySprites();
                                               recalculationNeeded = false;
                                           };

            return result;
        }

        [EditorOutline(14, "RedDoorOpen", TDLLayers.Door, @"environment\door\red\open", 0, "doortiles", "single")]
        public static Entity RedDoorOpen() { return RedDoor(true); }

        [EditorOutline(8, "Door", TDLLayers.Door, @"environment\door\yellow\closed", 0, "doortiles", "single")]
        public static Entity Door(bool mIsBroken = false, bool mIsOff = false, List<int> mIDs = default(List<int>))
        {
            var result = new Entity(Tile) {Layer = TDLLayers.Door};

            var recalculationNeeded = true;

            var cSwitch = new TDCSwitch(mIsOff) {OffTextureName = @"environment\door\yellow\open", OnTextureName = @"environment\door\yellow\closed"};
            var cRender = new TDCRender(Assets.Tilesets["doortiles"].GetSprite("single", Assets.GetTexture(@"environment\door\yellow\closed")));
            var cHitByWeapon = new TDCHitByWeapon(mWeapon =>
                                                  {
                                                      if (cSwitch.IsOff) return;
                                                      TDLMethods.Break(mIsBroken, result);
                                                  });
            var cID = mIDs != null ? new TDCID(mIDs.ToArray()) : new TDCID(-1);
            var cRecalculateSprites = new TDCRecalculateSprites(TDLTags.DoorClosed, TDLHelper.RecalculateDoorSprite, x => mIDs.All(x.GetComponent<TDCID>().IDs.Contains));
            var cIDSwitchAI = new TDCIDSwitchAI(cSwitch, cID);
            var cSwitchRecalculateTagAI = new TDCSwitchRecalculateTagAI(cSwitch, cRecalculateSprites, TDLTags.DoorOpen, TDLTags.DoorClosed);

            cSwitch.OnlyOffTags.Add(TDLTags.DoorOpen);
            cSwitch.OnlyOnTags.AddRange(new[] {TDLTags.DoorClosed, TDLTags.Solid});

            if (mIsBroken) TDLMethods.AttachBrokenOverlay(cRender);

            cSwitch.OnTurnOff += () => recalculationNeeded = true;
            cSwitch.OnTurnOn += () => recalculationNeeded = true;

            result.AddTags(TDLTags.Door, TDLTags.HitByWeapon);
            result.AddComponents(cRender, cHitByWeapon, cID, cSwitch, cSwitchRecalculateTagAI, cRecalculateSprites,
                                 cIDSwitchAI);

            result.Field.OnTurnFinished += () =>
                                           {
                                               if (!recalculationNeeded) return;
                                               cRecalculateSprites.RecalculateNearbySprites();
                                               recalculationNeeded = false;
                                           };

            return result;
        }

        [EditorOutline(9, "DoorOpen", TDLLayers.Door, @"environment\door\yellow\open", 0, "doortiles", "single")]
        public static Entity DoorOpen(bool mIsBroken = false, List<int> mIDs = default(List<int>)) { return Door(mIsBroken, true, mIDs); }

        [EditorOutline(10, "Orb", TDLLayers.Orb, @"elements\orb", 2)]
        public static Entity Orb(List<int> mTargetIDs = default(List<int>), List<int> mTargetEffects = default(List<int>), bool mIsBroken = false, int mHealth = 2)
        {
            var result = new Entity(Tile) {Layer = TDLLayers.Orb};

            var struckTime = 0;
            var health = mHealth; // 2 = cracked, 1 = broken, 0 = destroy!

            var cRender = new TDCRender(Assets.Tilesets["orbtiles"].GetSprite("on", Assets.GetTexture(@"elements\orb")));
            var cIDCaller = new TDCIDCaller(mTargetIDs, mTargetEffects);

            Action<TDCWeapon> struck = mWeapon =>
                                       {
                                           if (mIsBroken)
                                           {
                                               TDLHelper.PlaySound("SoundBrokenWall");
                                               health--;

                                               if (health < 2) cRender.GetSprite(1).TextureRect = Assets.Tilesets["brokenoverlaytiles"].GetTextureRect(1, 0);
                                           }

                                           if (health == 0)
                                           {
                                               result.Destroy();
                                               return;
                                           }

                                           TDLHelper.PlaySound("SoundOrbHit");
                                           struckTime = 10;
                                           cIDCaller.SendCalls();
                                       };

            if (mIsBroken) TDLMethods.AttachCrackedOverlay(cRender, mHealth >= 2 ? 0 : 1);

            var cHitByWeapon = new TDCHitByWeapon(struck);

            cRender.OnDraw += () =>
                              {
                                  if (struckTime > 0)
                                  {
                                      cRender.GetSprite(0).TextureRect = Assets.Tilesets["orbtiles"].GetTextureRect("struck");
                                      struckTime--;
                                  }
                                  else
                                      cRender.GetSprite(0).TextureRect = Assets.Tilesets["orbtiles"].GetTextureRect("on");
                              };

            result.AddTags(TDLTags.Solid, TDLTags.HitByWeapon);
            result.AddComponents(cRender, cHitByWeapon, cIDCaller);

            return result;
        }

        [EditorOutline(11, "Arrow", TDLLayers.Arrow, @"environment\arrow", 2)]
        public static Entity Arrow(int mDirection = 0, bool mIsOff = false, List<int> mIDs = default(List<int>))
        {
            var result = new Entity(Tile) {Layer = TDLLayers.Arrow, UpdateOrder = TDLOrders.Trapdoor};

            var cRender = new TDCRender(Assets.Tilesets["onoffdirtiles"].GetSprite("on_n", Assets.GetTexture(@"environment\arrow")));
            var cDirection = new TDCDirection {Direction = mDirection};
            var cSpecialSquare = new TDCSpecialSquare();
            var cID = mIDs != null ? new TDCID(mIDs.ToArray()) : new TDCID(-1);
            var cSwitch = new TDCSwitch(mIsOff)
                          {
                              OffTextureRect = Assets.Tilesets["onoffdirtiles"].GetTextureRect("off_" + cDirection.DirectionString),
                              OnTextureRect = Assets.Tilesets["onoffdirtiles"].GetTextureRect("on_" + cDirection.DirectionString)
                          };
            var cIDSwitchAI = new TDCIDSwitchAI(cSwitch, cID);

            cSpecialSquare.OnMoveFromAllowed += (Entity mEntity, int mNextX, int mNextY, bool mNextSuccess, out bool mAbort) => TDLMethods.ArrowMovement(mNextX, mNextY, cDirection, cSwitch, out mAbort);
            cSpecialSquare.OnMoveToAllowed += (Entity mEntity, int mNextX, int mNextY, bool mNextSuccess, out bool mAbort) => TDLMethods.ArrowMovement(mNextX, mNextY, cDirection, cSwitch, out mAbort);

            result.AddTags(TDLTags.Arrow);
            result.AddComponents(cRender, cDirection, cSpecialSquare, cID, cSwitch, cIDSwitchAI);

            return result;
        }

        [EditorOutline(15, "PressurePlateSingleUse", TDLLayers.Floor, @"environment\pressureplate", 0, "pressureplatetiles", "single")]
        public static Entity PressurePlateSingleUse(List<int> mTargetIDs = default(List<int>), List<int> mTargetEffects = default(List<int>))
        {
            var result = new Entity(Tile) {Layer = TDLLayers.Floor, UpdateOrder = TDLOrders.Trapdoor};

            var cRender = new TDCRender(Assets.Tilesets["pressureplatetiles"].GetSprite("single", Assets.GetTexture(@"environment\pressureplate")));
            var cSpecialSquare = new TDCSpecialSquare();
            var cIDCaller = new TDCIDCaller(mTargetIDs, mTargetEffects);
            var cFloodFiller = new TDCFloodFiller(TDLTags.PressurePlateSingleUse);
            var cBehavior = new TDCBehavior();
            var cPressurePlate = new TDCPressurePlate(cFloodFiller, cIDCaller);

            cRender.OnDraw += () => cRender.GetSprite(0).TextureRect = Assets.Tilesets["pressureplatetiles"].GetTextureRect(cPressurePlate.Triggered ? "triggered_single" : "single");

            cSpecialSquare.OnMoveToAllowed += TDLMethods.PressurePlateMovement(cPressurePlate);

            cBehavior.OnNextTurn += mInput => TDLMethods.TryTriggerPlate(cPressurePlate, result);

            result.AddTags(TDLTags.PressurePlateSingleUse);
            result.AddComponents(cRender, cPressurePlate, cSpecialSquare, cIDCaller, cFloodFiller, cBehavior);

            return result;
        }

        [EditorOutline(16, "PressurePlateMultipleUse", TDLLayers.Floor, @"environment\pressureplate", 0, "pressureplatetiles", "multiple")]
        public static Entity PressurePlateMultipleUse(List<int> mTargetIDs = default(List<int>), List<int> mTargetEffects = default(List<int>))
        {
            var result = new Entity(Tile) {Layer = TDLLayers.Floor, UpdateOrder = TDLOrders.Trapdoor};

            var cRender = new TDCRender(Assets.Tilesets["pressureplatetiles"].GetSprite("multiple", Assets.GetTexture(@"environment\pressureplate")));
            var cIDCaller = new TDCIDCaller(mTargetIDs, mTargetEffects);
            var cBehavior = new TDCBehavior();
            var cFloodFiller = new TDCFloodFiller(TDLTags.PressurePlateMultipleUse);
            var cSpecialSquare = new TDCSpecialSquare();
            var cPressurePlate = new TDCPressurePlate(cFloodFiller, cIDCaller);

            cRender.OnDraw += () => cRender.GetSprite(0).TextureRect = Assets.Tilesets["pressureplatetiles"].GetTextureRect(cPressurePlate.Triggered ? "triggered_multiple" : "multiple");

            cSpecialSquare.OnMoveToAllowed += TDLMethods.PressurePlateMovement(cPressurePlate);

            cBehavior.OnNextTurn += mInput =>
                                    {
                                        TDLMethods.TryTriggerPlate(cPressurePlate, result);

                                        var occupied = cPressurePlate.IsAnyNeighborOccupied();
                                        cPressurePlate.Triggered = occupied;

                                        if (!occupied)
                                            foreach (var plate in cFloodFiller.GetAttachedEntities())
                                                plate.GetComponent<TDCPressurePlate>().CallSent = false;
                                    };

            result.AddTags(TDLTags.PressurePlateMultipleUse);
            result.AddComponents(cRender, cSpecialSquare, cPressurePlate, cIDCaller, cBehavior, cFloodFiller);

            return result;
        }

        [EditorOutline(17, "PressurePlateOnOffUse", TDLLayers.Floor, @"environment\pressureplate", 0, "pressureplatetiles", "onoff")]
        public static Entity PressurePlateOnOffUse(List<int> mTargetIDs = default(List<int>), List<int> mTargetEffects = default(List<int>))
        {
            var result = new Entity(Tile) {Layer = TDLLayers.Floor, UpdateOrder = TDLOrders.Trapdoor};

            var cRender = new TDCRender(Assets.Tilesets["pressureplatetiles"].GetSprite("onoff", Assets.GetTexture(@"environment\pressureplate")));
            var cIDCaller = new TDCIDCaller(mTargetIDs, mTargetEffects);
            var cSpecialSquare = new TDCSpecialSquare();
            var cBehavior = new TDCBehavior();
            var cFloodFiller = new TDCFloodFiller(TDLTags.PressurePlateOnOffUse);
            var cPressurePlate = new TDCPressurePlate(cFloodFiller, cIDCaller);

            cRender.OnDraw += () => cRender.GetSprite(0).TextureRect =
                                    Assets.Tilesets["pressureplatetiles"].GetTextureRect(cPressurePlate.Triggered ? "triggered_onoff" : "onoff");

            cSpecialSquare.OnMoveToAllowed += TDLMethods.PressurePlateMovement(cPressurePlate);

            cBehavior.OnNextTurn += mInput =>
                                    {
                                        TDLMethods.TryTriggerPlate(cPressurePlate, result);

                                        if (cPressurePlate.IsAnyNeighborOccupied()) return;
                                        if (cPressurePlate.CallSent)
                                        {
                                            foreach (var plate in cFloodFiller.GetAttachedEntities())
                                                plate.GetComponent<TDCPressurePlate>().CallSent = false;

                                            cIDCaller.SendCalls();
                                            TDLHelper.PlaySound("SoundPressurePlate");
                                        }

                                        foreach (var plate in cFloodFiller.GetAttachedEntities())
                                        {
                                            plate.GetComponent<TDCPressurePlate>().CallSent = false;
                                            plate.GetComponent<TDCPressurePlate>().Triggered = false;
                                        }
                                    };

            result.AddTags(TDLTags.PressurePlateOnOffUse);
            result.AddComponents(cRender, cSpecialSquare, cPressurePlate, cIDCaller, cBehavior, cFloodFiller);

            return result;
        }

        [EditorOutline(20, "Scroll", TDLLayers.Arrow, @"elements\scroll", 1)]
        public static Entity Scroll(string mText = "")
        {
            var result = new Entity(Tile) {Layer = TDLLayers.Arrow, UpdateOrder = TDLOrders.Trapdoor};

            var read = false;

            var cRender = new TDCRender(new Sprite(Assets.GetTexture(@"elements\scroll")));
            var cBehavior = new TDCBehavior();

            cBehavior.OnNextTurn += mInput =>
                                    {
                                        if (result.Field.HasEntityByTag(result.X, result.Y, TDLTags.Player))
                                        {
                                            Game.SetScrollText(mText);
                                            if (!read)
                                            {
                                                TDLHelper.PlaySound("SoundScroll");
                                                read = true;
                                            }
                                        }
                                        else
                                            read = false;
                                    };

            result.AddTags(TDLTags.OrthogonalSquare);
            result.AddComponents(cRender, cBehavior);
            result.Field.OnTurnStart += () => Game.SetScrollText("");

            return result;
        }

        [EditorOutline(22, "Tunnel", TDLLayers.Arrow, @"elements\tunnel", 1)]
        public static Entity Tunnel(int mDirection = 0)
        {
            var result = new Entity(Tile) {Layer = TDLLayers.Arrow};

            var cRender = new TDCRender(new Sprite(Assets.GetTexture(@"elements\tunnel")));
            var cDirection = new TDCDirection {Direction = mDirection};
            var cSpecialSquare = new TDCSpecialSquare();

            cRender.OnDraw += () => cRender.GetSprite(0).TextureRect = Assets.Tilesets["dirtiles"].GetTextureRect(cDirection.DirectionString);

            cSpecialSquare.OnMoveToAllowed += (Entity mEntity, int mNextX, int mNextY, bool mNextSuccess, out bool mAbort) =>
                                              {
                                                  mAbort = false;

                                                  var isTunnelUser = mEntity.HasTag(TDLTags.TunnelUser);
                                                  if (isTunnelUser) return true;

                                                  var cKiller = mEntity.GetComponent<TDCKiller>();
                                                  return cKiller != null && cKiller.TargetTags.Any(x => result.Field.HasEntityByTag(result.X, result.Y, x));
                                              };
            cSpecialSquare.OnMoveFromAllowed += (Entity mEntity, int mNextX, int mNextY, bool mNextSuccess, out bool mAbort) =>
                                                {
                                                    mAbort = false;

                                                    if (!mEntity.HasTag(TDLTags.TunnelUser)) return true;

                                                    var directionVector = cDirection.DirectionVector;

                                                    if (mNextX == directionVector.X && mNextY == directionVector.Y)
                                                    {
                                                        TDLHelper.PlaySound("SoundTunnel");

                                                        var rayX = result.X;
                                                        var rayY = result.Y;
                                                        while (true)
                                                        {
                                                            rayX += directionVector.X;
                                                            rayY += directionVector.Y;
                                                            if (!result.Field.IsTileValid(rayX, rayY)) break;
                                                            if (result.Field.HasEntityByTag(rayX, rayY, TDLTags.Tunnel)) goto tunnelFound;
                                                        }

                                                        rayX = result.X;
                                                        rayY = result.Y;
                                                        while (true)
                                                        {
                                                            rayX -= directionVector.X;
                                                            rayY -= directionVector.Y;
                                                            if (!result.Field.IsTileValid(rayX, rayY)) return false;
                                                            if (result.Field.HasEntityByTag(rayX, rayY, TDLTags.Tunnel)) goto tunnelFound;
                                                        }

                                                        tunnelFound:
                                                        mAbort = true;
                                                        if (TDLTags.GroundAllowedTags.Any(x => result.Field.HasEntityByTag(rayX, rayY, x)))
                                                            if (!TDLTags.GroundObstacleTags.Any(x => result.Field.HasEntityByTag(rayX, rayY, x)))
                                                                foreach (var entity in new List<Entity>(result.Field.GetEntitiesByTag(result.X, result.Y, TDLTags.TunnelUser)))
                                                                    entity.Move(rayX, rayY);

                                                        return false;
                                                    }

                                                    return true;
                                                };

            result.AddTags(TDLTags.Tunnel);
            result.AddComponents(cRender, cSpecialSquare, cDirection);

            return result;
        }

        [EditorOutline(23, "Booster", TDLLayers.Arrow, @"elements\booster", 1)]
        public static Entity Booster(int mDirection = 0, bool mIsOff = false, List<int> mIDs = default(List<int>))
        {
            var result = new Entity(Tile) {Layer = TDLLayers.Arrow};

            var cRender = new TDCRender(Assets.Tilesets["onoffdirtiles"].GetSprite("on_n", Assets.GetTexture(@"elements\booster")));
            var cDirection = new TDCDirection {Direction = mDirection};
            var cSpecialSquare = new TDCSpecialSquare();
            var cID = mIDs != null ? new TDCID(mIDs.ToArray()) : new TDCID(-1);
            var cSwitch = new TDCSwitch(mIsOff)
                          {
                              OffTextureRect = Assets.Tilesets["onoffdirtiles"].GetTextureRect("off_" + cDirection.DirectionString),
                              OnTextureRect = Assets.Tilesets["onoffdirtiles"].GetTextureRect("on_" + cDirection.DirectionString)
                          };
            var cIDSwitchAI = new TDCIDSwitchAI(cSwitch, cID);

            cSpecialSquare.OnMoveFromAllowed += (Entity mEntity, int mNextX, int mNextY, bool mNextSuccess, out bool mAbort) =>
                                                {
                                                    mAbort = false;

                                                    if (!mEntity.HasTag(TDLTags.BoosterUser) || cSwitch.IsOff) return true;

                                                    var directionVector = cDirection.DirectionVector;

                                                    if (mNextX == directionVector.X && mNextY == directionVector.Y)
                                                    {
                                                        int rayX = result.X, rayY = result.Y;
                                                        while (true)
                                                        {
                                                            rayX += directionVector.X;
                                                            rayY += directionVector.Y;
                                                            if (!result.Field.IsTileValid(rayX, rayY)) break;
                                                            if (!result.Field.HasEntityByTag(rayX, rayY, TDLTags.Booster)) continue;

                                                            var valid = true;

                                                            foreach (var booster in result.Field.GetEntitiesByTag(rayX, rayY, TDLTags.Booster))
                                                                if (booster.GetComponent<TDCSwitch>().IsOff) valid = false;

                                                            if (valid)
                                                                goto boosterFound;
                                                        }
                                                        return true;

                                                        boosterFound:
                                                        if (TDLTags.GroundAllowedTags.Any(x => result.Field.HasEntityByTag(rayX, rayY, x)))
                                                            if (!TDLTags.GroundObstacleTags.Any(x => result.Field.HasEntityByTag(rayX, rayY, x)))
                                                            {
                                                                TDLHelper.PlaySound("SoundTunnel");
                                                                mAbort = true;

                                                                foreach (var entity in new List<Entity>(result.Field.GetEntities(result.X, result.Y).Where(x => x.HasTag(TDLTags.BoosterUser))))
                                                                    entity.Move(rayX, rayY);

                                                                return false;
                                                            }
                                                    }

                                                    return true;
                                                };

            result.AddTags(TDLTags.Booster);
            result.AddComponents(cRender, cSpecialSquare, cDirection, cID, cSwitch, cIDSwitchAI);

            return result;
        }

        [EditorOutline(24, "Water", TDLLayers.Pit, @"environment\water", 0, "doortiles", "fill")]
        public static Entity Water()
        {
            var result = new Entity(Tile) {Layer = TDLLayers.Pit};

            var cRender = new TDCRender(Assets.Tilesets["doortiles"].GetSprite("single", Assets.GetTexture(@"environment\water")));
            var cRecalculateSprites = new TDCRecalculateSprites(TDLTags.Water, TDLHelper.RecalculateDoorSprite);

            result.AddTags(TDLTags.Solid, TDLTags.Water);
            result.AddComponents(cRender, cRecalculateSprites);

            return result;
        }

        [EditorOutline(26, "Oremites", TDLLayers.Trapdoor, @"environment\oremites", 0, "doortiles", "fill")]
        public static Entity Oremites()
        {
            var result = new Entity(Tile) {Layer = TDLLayers.Trapdoor};

            var cRender = new TDCRender(Assets.Tilesets["doortiles"].GetSprite("single", Assets.GetTexture(@"environment\oremites")));
            var cRecalculateSprites = new TDCRecalculateSprites(TDLTags.Oremites, TDLHelper.RecalculateDoorSprite);
            var cSpecialSquare = new TDCSpecialSquare();
            var cBehavior = new TDCBehavior();

            Func<Entity, bool> sheateEntity = mEntity =>
                                              {
                                                  var cWielder = mEntity.GetComponent<TDCWielder>();
                                                  if (!cWielder.WeaponComponent.Entity.HasTag(TDLTags.AffectedByOremites)) return false;
                                                  cWielder.Sheated = true;
                                                  return true;
                                              };

            cSpecialSquare.OnMoveFromAllowed += (Entity mEntity, int mNextX, int mNextY, bool mNextSuccess, out bool mAbort) =>
                                                {
                                                    mAbort = false;

                                                    var cWielder = mEntity.GetComponent<TDCWielder>();
                                                    if (!mNextSuccess || cWielder == null) return true;

                                                    var weaponComponent = cWielder.WeaponComponent;
                                                    if (!weaponComponent.Entity.HasTag(TDLTags.AffectedByOremites)) return true;

                                                    cWielder.Sheated = false;

                                                    return true;
                                                };

            cSpecialSquare.OnMoveToAllowed += (Entity mEntity, int mNextX, int mNextY, bool mNextSuccess, out bool mAbort) =>
                                              {
                                                  mAbort = false;

                                                  if (mEntity.GetComponent<TDCWielder>() == null) return true;
                                                  if (!sheateEntity(mEntity) || !mNextSuccess) return true;

                                                  mEntity.GetComponent<TDCDirection>().Direction = TDCDirection.GetDirection(new Vector2i(mNextX, mNextY));

                                                  return true;
                                              };

            cBehavior.OnNextTurn += mInput =>
                                    {
                                        foreach (var entity in result.Field.GetEntitiesByComponent(result.X, result.Y, typeof (TDCWielder)))
                                            sheateEntity(entity);
                                    };

            result.AddTags(TDLTags.Oremites);
            result.AddComponents(cRender, cSpecialSquare, cBehavior, cRecalculateSprites);

            return result;
        }

        [EditorOutline(27, "HotTile", TDLLayers.Trapdoor, @"environment\hottile", 0, "doortiles", "fill")]
        public static Entity HotTile(int mTurnsToBurn = 2)
        {
            var result = new Entity(Tile) {UpdateOrder = TDLOrders.Trapdoor, Layer = TDLLayers.Trapdoor};

            var onMeEntities = new List<Entity>();
            var onMeTurnsToBurn = new List<int>();

            var cRender = new TDCRender(Assets.Tilesets["doortiles"].GetSprite("single", Assets.GetTexture(@"environment\hottile")));
            var cRecalculateSprites = new TDCRecalculateSprites(TDLTags.HotTile, TDLHelper.RecalculateDoorSprite);
            var cSpecialSquare = new TDCSpecialSquare();
            var cBehavior = new TDCBehavior();

            cBehavior.OnNextTurn += mInput =>
                                    {
                                        var indexesToRemove = new List<int>();

                                        for (var i = 0; i < onMeEntities.Count; i++)
                                        {
                                            onMeTurnsToBurn[i] = onMeTurnsToBurn[i] - 1;

                                            if (onMeTurnsToBurn[i] != 0) continue;

                                            TDLMethods.Burn(onMeEntities[i]);
                                            indexesToRemove.Add(i);
                                        }

                                        foreach (var i in indexesToRemove)
                                        {
                                            onMeEntities.RemoveAt(i);
                                            onMeTurnsToBurn.RemoveAt(i);
                                        }
                                    };

            cSpecialSquare.OnMoveFromAllowed += (Entity mEntity, int mNextX, int mNextY, bool mNextSuccess, out bool mAbort) =>
                                                {
                                                    mAbort = false;
                                                    if (!mNextSuccess) return true;

                                                    var indexesToRemove = new List<int>();

                                                    for (var i = 0; i < onMeEntities.Count; i++)
                                                        if (onMeEntities[i] == mEntity)
                                                            indexesToRemove.Add(i);

                                                    foreach (var i in indexesToRemove)
                                                    {
                                                        onMeEntities.RemoveAt(i);
                                                        onMeTurnsToBurn.RemoveAt(i);
                                                    }
                                                    return true;
                                                };

            cSpecialSquare.OnMoveToAllowed += (Entity mEntity, int mNextX, int mNextY, bool mNextSuccess, out bool mAbort) =>
                                              {
                                                  mAbort = false;
                                                  if (!mNextSuccess) return true;

                                                  if (!mEntity.HasTag(TDLTags.NotAffectedByHeat))
                                                      if (!onMeEntities.Contains(mEntity))
                                                      {
                                                          onMeEntities.Add(mEntity);
                                                          onMeTurnsToBurn.Add(mTurnsToBurn);
                                                      }

                                                  return true;
                                              };

            result.AddTags(TDLTags.HotTile);
            result.AddComponents(cRender, cSpecialSquare, cRecalculateSprites, cBehavior);

            return result;
        }

        [EditorOutline(28, "Troll", TDLLayers.Roach, @"humanoid\body\purple", 3)]
        public static Entity Troll(int mDirection = 0)
        {
            var result = new Entity(Tile) {Layer = TDLLayers.Roach, UpdateOrder = TDLOrders.Guard};

            var cRender = new TDCRender(
                Assets.Tilesets["humanoidtiles"].GetSprite("n", Assets.GetTexture(@"humanoid\body\purple")),
                Assets.Tilesets["humanoidtiles"].GetSprite("n", Assets.GetTexture(@"humanoid\hands\pink")),
                Assets.Tilesets["humanoidtiles"].GetSprite("n", Assets.GetTexture(@"humanoid\head\pink"))) {IsLerped = true};
            var cDirection = new TDCDirection {Direction = mDirection};
            var cTarget = new TDCTarget(TDLTags.MonsterTargetTags);
            var cHitByWeapon = new TDCHitByWeapon(mWeapon => TDLMethods.Kill(result));
            var cBehavior = new TDCBehavior();
            var cMovement = new TDCMovement(TDCMovement.MovementType.FlexibleNormal, TDLTags.GroundAllowedTags, TDLTags.GroundObstacleTags, TDLTags.GroundExceptionTags);
            var cRenderDirectionAI = new TDCRenderDirectionAI(cRender, cDirection, "humanoidtiles");
            var cWielder = new TDCWielder(cMovement, cDirection);

            result.AddTags(TDLTags.Solid, TDLTags.Humanoid, TDLTags.SideBad, TDLTags.HitByWeapon,
                           TDLTags.WeightLow, TDLTags.BoosterUser, TDLTags.RequiredKill);
            result.AddComponents(cRender, cDirection, cTarget, cHitByWeapon,
                                 cBehavior, cMovement, cRenderDirectionAI, cWielder);

            cWielder.SetWeapon(WoodenSword());

            cRender.OnDraw += () => { cRenderDirectionAI.Prefix = cWielder.Sheated ? "sheated_" : ""; };

            cBehavior.OnNextTurn += mInput =>
                                    {
                                        if (cTarget.Target == null) return;
                                        var preferredDirection = TDCDirection.GetDirection(new Vector2i(cTarget.TargetNextX, cTarget.TargetNextY));

                                        if (!cWielder.Sheated && cDirection.Direction != preferredDirection)
                                        {
                                            cDirection.Direction = TDCDirection.GetTurningDirection(cDirection.Direction, preferredDirection);
                                            cWielder.SyncWeaponWithWielder();
                                        }
                                        else
                                        {
                                            cMovement.TargetX = cTarget.Target.X;
                                            cMovement.TargetY = cTarget.Target.Y;
                                        }

                                        if (cWielder.Sheated)
                                            cDirection.Direction = TDCDirection.GetDirection(new Vector2i(cTarget.TargetNextX, cTarget.TargetNextY));
                                    };
            return result;
        }

        [EditorOutline(29, "WeaponSlot", TDLLayers.Arrow, @"elements\weaponslot", 1)]
        public static Entity WeaponSlot(bool mIsOff = false, List<int> mIDs = default(List<int>), string mWeapon = "")
        {
            var result = new Entity(Tile) {Layer = TDLLayers.Arrow, UpdateOrder = TDLOrders.Trapdoor};

            var cRender = new TDCRender(Assets.Tilesets["onofftiles"].GetSprite("on", Assets.GetTexture(@"elements\weaponslot")));
            var cSpecialSquare = new TDCSpecialSquare();
            var cID = mIDs != null ? new TDCID(mIDs.ToArray()) : new TDCID(-1);
            var cSwitch = new TDCSwitch(mIsOff) {OffTextureRect = Assets.Tilesets["onofftiles"].GetTextureRect("off"), OnTextureRect = Assets.Tilesets["onofftiles"].GetTextureRect("on")};
            var cIDSwitchAI = new TDCIDSwitchAI(cSwitch, cID);

            cRender.Sprites.Add(new Sprite());

            Entity onMeWeapon = null;

            Action recalculateWeaponSprite = () =>
                                             {
                                                 if (onMeWeapon == null)
                                                 {
                                                     cRender.GetSprite(1).Scale = new Vector2f(0, 0);
                                                     return;
                                                 }
                                                 cRender.SetSprite(1, Assets.Tilesets["dirtiles"].GetSprite("n", onMeWeapon.GetComponent<TDCRender>().GetSprite(0).Texture));
                                                 cRender.GetSprite(1).Origin += new Vector2f(0, 2);
                                                 cRender.GetSprite(1).Rotation = 45;
                                                 cRender.GetSprite(1).Scale = new Vector2f(0.6f, 0.6f);
                                             };

            if (mWeapon == "reallybigsword")
                onMeWeapon = ReallyBigSword();
            else if (mWeapon == "shortsword")
                onMeWeapon = ShortSword();
            else if (mWeapon == "woodensword")
                onMeWeapon = WoodenSword();

            if (onMeWeapon != null) onMeWeapon.IsOutOfField = true;
            recalculateWeaponSprite();

            cSpecialSquare.OnMoveToAllowed += (Entity mEntity, int mNextX, int mNextY, bool mNextSuccess, out bool mAbort) =>
                                              {
                                                  mAbort = false;
                                                  if (!mNextSuccess) return true;

                                                  var cWielder = mEntity.GetComponent<TDCWielder>();
                                                  if (cWielder == null) return true;

                                                  TDLHelper.PlaySound("SoundMimic");

                                                  if (onMeWeapon != null)
                                                  {
                                                      var tempWeapon = cWielder.WeaponEntity;
                                                      cWielder.SetWeapon(onMeWeapon);
                                                      cWielder.Sheated = false;
                                                      onMeWeapon = tempWeapon;
                                                  }
                                                  else
                                                  {
                                                      cWielder.WeaponComponent.OnUnEquip.SafeInvoke(cWielder);
                                                      onMeWeapon = cWielder.WeaponEntity;
                                                      cWielder.SetWeapon(null);
                                                  }

                                                  if (onMeWeapon != null) onMeWeapon.IsOutOfField = true;
                                                  recalculateWeaponSprite();
                                                  return true;
                                              };

            result.AddTags(TDLTags.WeaponSlot);
            result.AddComponents(cRender, cSpecialSquare, cID, cSwitch, cIDSwitchAI);

            return result;
        }

        public static Entity Floor()
        {
            var result = new Entity(Tile, false) {Layer = TDLLayers.Floor};
            result.AddTags(TDLTags.GroundWalkable);
            return result;
        }

        public static Entity Player(int mDirection = 0)
        {
            var result = new Entity(Tile) {Layer = TDLLayers.Roach, UpdateOrder = TDLOrders.Player};

            var cRender = new TDCRender(
                Assets.Tilesets["humanoidtiles"].GetSprite("n", Assets.GetTexture(@"humanoid\body\yellow")),
                Assets.Tilesets["humanoidtiles"].GetSprite("n", Assets.GetTexture(@"humanoid\hands\pink")),
                Assets.Tilesets["humanoidtiles"].GetSprite("n", Assets.GetTexture(@"humanoid\head\pink"))) {IsLerped = true};
            var cDirection = new TDCDirection {Direction = mDirection};
            var cTarget = new TDCTarget();
            var cHitByWeapon = new TDCHitByWeapon(mWeapon => TDLMethods.Kill(result));
            var cBehavior = new TDCBehavior();
            var cMovement = new TDCMovement(TDCMovement.MovementType.Direct, TDLTags.GroundAllowedTags, TDLTags.GroundObstacleTags, TDLTags.GroundExceptionTags);
            var cRenderDirectionAI = new TDCRenderDirectionAI(cRender, cDirection, "humanoidtiles");
            var cWielder = new TDCWielder(cMovement, cDirection);

            result.AddTags(TDLTags.Solid, TDLTags.Humanoid, TDLTags.SideGood, TDLTags.HitByWeapon,
                           TDLTags.Player, TDLTags.TunnelUser, TDLTags.WeightLow, TDLTags.BoosterUser);
            result.AddComponents(cRender, cDirection, cTarget, cHitByWeapon, cBehavior, cMovement, cRenderDirectionAI, cWielder);

            cWielder.SetWeapon(ReallyBigSword());

            cRender.OnDraw += () => { cRenderDirectionAI.Prefix = cWielder.Sheated ? "sheated_" : ""; };

            cBehavior.OnNextTurn += mInput =>
                                    {
                                        var input = (int) mInput;

                                        if (input >= 0 && input <= 7)
                                        {
                                            var movementVector = TDCDirection.GetDirectionVector(input);
                                            cMovement.TargetX = result.X + movementVector.X;
                                            cMovement.TargetY = result.Y + movementVector.Y;
                                            if (cWielder.Sheated) cDirection.Direction = input;
                                            TDLHelper.PlaySound("SoundStep1");
                                        }
                                        else if (input != -1)
                                        {
                                            if (input == 8) cDirection.Direction--;
                                            if (input == 9) cDirection.Direction++;
                                            TDLHelper.PlaySound("SoundSwing");

                                            if (!cWielder.Sheated) cWielder.SyncWeaponWithWielder();
                                        }

                                        Game.Pathfinder.RefreshObstacles(0, 0, Field.Width, Field.Height); //test
                                    };

            cMovement.OnMovementSuccess += () => Game.Pathfinder.RefreshObstacles(0, 0, Field.Width, Field.Height); //test

            cMovement.OnMovementFail += () =>
                                        {
                                            // This is only to prevent wall bump sounds if using a tunnel or a booster
                                            var x = cMovement.TargetX;
                                            var y = cMovement.TargetY;

                                            if (result.Field.HasEntityByTag(result.X, result.Y, TDLTags.Tunnel)) return;
                                            if (result.Field.HasEntityByTag(result.X, result.Y, TDLTags.Booster)) return;

                                            if (result.Field.HasEntityByTag(x, y, TDLTags.Wall)) TDLHelper.PlaySound("SoundWallBump");
                                            else if (result.Field.HasEntityByTag(x, y, TDLTags.Pit)) TDLHelper.PlaySound("SoundPitBump");
                                        };

            return result;
        }

        public static Entity RoachEgg()
        {
            var result = new Entity(Tile) {Layer = TDLLayers.Roach, UpdateOrder = TDLOrders.Roach};

            var turnsToHatch = 4;

            var cRender = new TDCRender(Assets.Tilesets["roachtiles"].GetSprite("n", Assets.GetTexture(@"monster\roachegg")));
            var cHitByWeapon = new TDCHitByWeapon(mWeapon => TDLMethods.Kill(result));
            var cBehavior = new TDCBehavior();

            cRender.OnDraw += () => cRender.GetSprite(0).TextureRect = Assets.Tilesets["roachtiles"].GetTextureRect(4 - turnsToHatch, 0);

            cBehavior.OnNextTurn += mInput =>
                                    {
                                        turnsToHatch--;
                                        if (turnsToHatch != 0) return;
                                        result.Destroy();
                                        Tile = result.Field.GetTile(result.X, result.Y);
                                        result.Field.AddEntity(Roach());
                                    };

            result.AddTags(TDLTags.Solid, TDLTags.SideBad, TDLTags.HitByWeapon, TDLTags.RequiredKill);
            result.AddComponents(cRender, cHitByWeapon, cBehavior);

            return result;
        }

        public static Entity ReallyBigSword()
        {
            var result = new Entity(Tile) {Layer = TDLLayers.Weapon};

            var renderComponent = new TDCRender(Assets.Tilesets["dirtiles"].GetSprite("n", Assets.GetTexture(@"weapon\reallybigsword"))) {IsLerped = true};
            var cDirection = new TDCDirection();
            var weaponComponent = new TDCWeapon();
            var cRenderDirectionAI = new TDCRenderDirectionAI(renderComponent, cDirection, "dirtiles");

            weaponComponent.OnEquip += mWielder => mWielder.Entity.AddTags(TDLTags.WeightHigh);
            weaponComponent.OnUnEquip += mWielder => mWielder.Entity.RemoveTags(TDLTags.WeightHigh);

            result.AddTags(TDLTags.Solid, TDLTags.Weapon, TDLTags.DoesNotTriggerPlate, TDLTags.AffectedByOremites);
            result.AddComponents(renderComponent, cDirection, weaponComponent, cRenderDirectionAI);

            return result;
        }

        public static Entity ShortSword()
        {
            var result = new Entity(Tile) {Layer = TDLLayers.Weapon};

            var renderComponent = new TDCRender(Assets.Tilesets["dirtiles"].GetSprite("n", Assets.GetTexture(@"weapon\sword"))) {IsLerped = true};
            var cDirection = new TDCDirection();
            var weaponComponent = new TDCWeapon();
            var cRenderDirectionAI = new TDCRenderDirectionAI(renderComponent, cDirection, "dirtiles");

            result.AddTags(TDLTags.Solid, TDLTags.Weapon, TDLTags.DoesNotTriggerPlate, TDLTags.AffectedByOremites);
            result.AddComponents(renderComponent, cDirection, weaponComponent, cRenderDirectionAI);

            return result;
        }

        public static Entity WoodenSword()
        {
            var result = new Entity(Tile) {Layer = TDLLayers.Weapon};

            var renderComponent = new TDCRender(Assets.Tilesets["dirtiles"].GetSprite("n", Assets.GetTexture(@"weapon\woodensword"))) {IsLerped = true};
            var cDirection = new TDCDirection();
            var weaponComponent = new TDCWeapon();
            var cRenderDirectionAI = new TDCRenderDirectionAI(renderComponent, cDirection, "dirtiles");

            result.AddTags(TDLTags.Solid, TDLTags.Weapon, TDLTags.DoesNotTriggerPlate);
            result.AddComponents(renderComponent, cDirection, weaponComponent, cRenderDirectionAI);

            return result;
        }
    }
}